﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharedLib.MessageFramework.Messages;
using Tracy.DataModel;
using Tracy.Properties;
using System.IO;
using Tracy.BasicServices;
using SharedLib.Helpers;
using SharedLib.MessageFramework;
using System.Threading;
using SharedLib;
using Tracy.Messages;
using SharedLib.MessageFramework.Handlers;

namespace Tracy.Handlers
{
    public class FileMonitorHandler : TaskMessageHandler
    {
        public static event EventHandler<GenericEventArgs<IList<MediaFile>>> FilesUpdating;

        Dictionary<IAppMessage, ManualResetEvent> _manualResetEventDict = new Dictionary<IAppMessage, ManualResetEvent>();
        protected override void ProcessMessage(TaskMessage msg)
        {
            if (msg != null)
            {
                List<MediaFile> updatedFiles = new List<MediaFile>();
                if (msg.Data == null)
                {
                    CheckIncompleteFiles(msg, updatedFiles);
                }
                else if (msg.Data is MediaFile)
                {
                    MediaFile file = msg.Data as MediaFile;
                    if (CheckSingleFile(msg, file))
                    {
                        updatedFiles.Add(file);
                    }
                }
                else if (msg.Data is List<MediaFile>)
                {
                    foreach (MediaFile file in msg.Data as List<MediaFile>)
                    {
                        if (CheckSingleFile(msg, file))
                        {
                            updatedFiles.Add(file);
                        }
                    }
                }

                if (updatedFiles.Count > 0)
                {
                    //TracyFacade.Instance.MessageDispatcher.NotifyUI(msg, NotifyUIEventArgs.FILE_UPDATED, updatedFiles);
                    FilesUpdating.InvokeEvent(this, new GenericEventArgs<IList<MediaFile>>(updatedFiles));
                }

                //Wait all copy operations to be completed
                if (_manualResetEventDict.Count > 0)
                {
                    WaitHandle.WaitAll(_manualResetEventDict.Values.ToArray(), _manualResetEventDict.Count * 60 * 1000);
                }
            }
        }

        private bool FileExistsInPath(string path)
        {
            if (String.IsNullOrEmpty(path)) return false;
            if (File.Exists(path)) return true;

            //string filename = Path.GetFileName(path);
            //string filenameWithoutExt = filename;
            //int index = filename.IndexOf(".");
            //if(index > 0)
            //{
            //    filenameWithoutExt = filenameWithoutExt.Substring(0, index);
            //}
            //path = Path.Combine(Path.GetDirectoryName(path), filenameWithoutExt, filename);
            //if (File.Exists(path)) return path;
            return false;
        }

        private string FileExistsInPaths(string[] paths)
        {
            if (paths == null) return null;
            foreach (string path in paths)
            {
                if (FileExistsInPath(path)) return path;
            }
            return null;
        }

        /// <summary>
        /// Check file status
        /// </summary>
        /// <param name="mediaFile"></param>
        /// <returns>Is File Status Changed</returns>
        private bool CheckSingleFile(IAppMessage parentMsg, MediaFile file)
        {
            string[] fullPaths = file.GetTempFilePaths(); //File download already completed, but not moved to permanent path
            string[] tempFullPaths = TracyFacade.Instance.DownloadManager.GetDownloadingFilePaths(file); //File is still downloading
            string newFullPath = file.GetSavedFilePath(); //File is moved to permanent path
            string copyPath = null;
            switch (file.Status)
            {
                case "Missing":
                case "New":
                    if (File.Exists(newFullPath))
                    {
                        MarkAsReady(file);
                        return true;
                    }
                    else
                    {
                        copyPath = FileExistsInPaths(fullPaths);
                        if (copyPath != null)
                        {
                            file.Status = "Copying";
                            BeginCopyFile(parentMsg, file, copyPath, newFullPath);
                            return true;
                        }
                        else if (FileExistsInPaths(tempFullPaths) != null)
                        {
                            file.Status = "In Progress";
                            return true;
                        }
                    }
                    break;

                case "In Progress":
                    if (File.Exists(newFullPath))
                    {
                        file.Status = "Ready";
                        return true;
                    }
                    else
                    {
                        copyPath = FileExistsInPaths(fullPaths);
                        if (copyPath != null)
                        {
                            file.Status = "Copying";
                            BeginCopyFile(parentMsg, file, copyPath, newFullPath);
                            return true;
                        }
                        else if (FileExistsInPaths(tempFullPaths) == null)
                        {
                            file.Status = "Missing";
                            return true;
                        }
                    }
                    break;
                case "Copying":
                case "Moving":
                    if (File.Exists(newFullPath))
                    {
                        MarkAsReady(file);
                        return true;
                    }
                    else
                    {
                        copyPath = FileExistsInPaths(fullPaths);
                        if (copyPath != null)
                        {
                            BeginCopyFile(parentMsg, file, copyPath, newFullPath, file.Status == "Copying");
                            return false;
                        }
                        else
                        {
                            file.Status = "Missing";
                            return true;
                        }
                    }
                case "Ready":
                case "Watched":
                    if (!File.Exists(newFullPath))
                    {
                        file.Properties[MediaFile.KEY_PREVIOUS_STATUS] = file.Status;
                        file.Status = "Missing";//TODO: Support removable device
                        return true;
                    }
                    else
                    {
                        return ClearTempFile(file);
                    }
                    //break;
            }
            return false;
        }

        private bool ClearTempFile(MediaFile file)
        {
            if (file != null && (file.Status == "Ready" || file.Status == "Watched"))
            {
                //All files of the same resource should complete downloading
                foreach (MediaFile fileOfSameResource in TracyFacade.Instance.MediaFileProvider.GetItemsByResource(file.Resource))
                {
                    if (file.Status != "Ready" && file.Status != "Watched")
                    {
                        return false;
                    }
                }

                string[] downloadPaths = file.GetTempFilePaths();
                string path = FileExistsInPaths(downloadPaths);
                if (!String.IsNullOrEmpty(path))//If download path exists
                {
                    if (String.IsNullOrEmpty(file.CompleteDate))
                    {
                        file.CompleteDate = DateTime.Now.ToString();
                        return true;
                    }

                    DateTime completeDate = DateTime.Parse(file.CompleteDate);
                    if (completeDate.AddHours(24) < DateTime.Now) //If already completed for 24 hours
                    {
                        //temp download file exists
                        try
                        {
                            if (!String.Equals(file.GetSavedFilePath(), path, StringComparison.OrdinalIgnoreCase))
                            {
                                File.SetAttributes(path, FileAttributes.Normal);
                                File.Delete(path);
                            }
                            //Workaround: Load latest status since it might be changed by other items
                            //TODO: Use entity framework or change flag to handle attribute change
                            MediaFile currentFile = TracyFacade.Instance.MediaFileProvider.GetItemByID(file.ID);
                            file.Status = currentFile.Status;
                            file.Properties.Remove(MediaFile.KEY_DOWNLOAD_PATH);//Remove download path
                            return true;
                        }
                        catch (Exception ex)
                        {
                            //TODO:Log only
                        }

                    }
                }
            }
            return false;
        }


        private void MarkAsReady(MediaFile file)
        {
            if (file.Properties.ContainsKey(MediaFile.KEY_PREVIOUS_STATUS))
            {
                file.Status = file.Properties[MediaFile.KEY_PREVIOUS_STATUS];
                file.Properties.Remove(MediaFile.KEY_PREVIOUS_STATUS);
            }
            else
            {
                file.Status = "Ready";
            }
            if (String.IsNullOrEmpty(file.CompleteDate))
            {
                file.CompleteDate = DateTime.Now.ToString();
            }
        }

        private void CheckIncompleteFiles(IAppMessage parentMsg, List<MediaFile> updatedFiles)
        {
            foreach (MediaFile file in TracyFacade.Instance.MediaFileProvider.GetIncompleteFiles())
            {
                if (CheckSingleFile(parentMsg, file)) updatedFiles.Add(file);
            }
        }

        private void BeginCopyFile(IAppMessage parentMsg, MediaFile file, string sourceFile, string destinationFile, bool copyFile = true)
        {
            ManualResetEvent mre = new ManualResetEvent(false);
            CopyFileMessage msg = new CopyFileMessage(this, sourceFile, destinationFile, file, parentMsg);
            msg.CallBack = EndCopyFile;
            _manualResetEventDict.Add(msg, mre);
            if (!copyFile) msg.Mode = CopyFileMessage.CopyFileMode.Move;
            TracyFacade.Instance.MessageDispatcher.EnqueueMessage(msg);
        }

        private void EndCopyFile(IAppMessage msg)
        {
            if (msg != null && msg.Data != null && msg.Data is MediaFile)
            {
                
                List<MediaFile> updatedFiles = new List<MediaFile>();
                MediaFile copiedFile = msg.Data as MediaFile;
                Console.WriteLine("[" + DateTime.Now.ToString("HH:mm:ss fff") + "] File copy completed: " + copiedFile.FileName); 
                MarkAsReady(copiedFile);
                updatedFiles.Add(copiedFile);
                FilesUpdating.InvokeEvent(this, new GenericEventArgs<IList<MediaFile>>(updatedFiles));
                _manualResetEventDict[msg].Set();
            }
        }
    }
}
